home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-03-10 | 8.1 KB | 255 lines | [TEXT/ALFA] |
- // This may look like C code, but it is really -*- C++ -*-
- /*
- ************************************************************************
- *
- * Arithmetic Coding
- *
- * The functions implemented here perform actual variable bit decoding/encoding
- * of an item (an integer number) using the cumulative probabilities supplied
- * by a particular model of the source data.
- *
- * $Id: arithm_coding.cc,v 2.2 1995/05/31 16:23:24 oleg Exp oleg $
- *
- ************************************************************************
- */
-
- #ifdef __GNUC__
- #pragma implementation "arithm.h"
- #endif
- #include "arithm.h"
-
- /*
- *------------------------------------------------------------------------
- * Constructors and Destructors
- * Note, constructor doesn't yet know if encoding or decoding is to be
- * performed. Nor does it open the bit stream. You have to open the
- * stream explicitly using the appropriate open() function (see class BitIO and
- * EndianIO).
- */
-
- // Just initialize the data fields
- ArithmCodingData::ArithmCodingData(Input_Data_Model& model)
- : Source_model(model)
- {
- low = 0; // Start with the full code range
- high = Top_value;
- bits_to_follow = 0; // Nothing to follow yet
- value = 0;
- assert( Half == 1<<(Code_size-1) && Third_qtr == Half | First_qtr );
- Coding_status = Init;
- }
-
-
- // Initialization
- void ArithmCodingOut::initialize(void)
- {
- Source_model.open((BitOut&)(*this));
- Source_model.agree_on_top_freq( (1<<(Code_size-2)) - 1 );
- Coding_status = Coding;
- }
-
- void ArithmCodingIn::initialize(void)
- {
- Source_model.open((BitIn&)(*this));
- register int i; // Read in the first code value
- for(i=0; i<Code_size; i++)
- value = (value << 1) | get_bit();;
- Source_model.agree_on_top_freq( (1<<(Code_size-2)) - 1 );
- Coding_status = Coding;
- }
-
- /*
- *------------------------------------------------------------------------
- * Handling the coded stream
- */
-
- // Close the coded stream
- void ArithmCodingOut::close(void)
- {
- if( Coding_status == EoF )
- return; // Already closed
- Coding_status = EoF;
- encode_index(Source_model.eof_index());
- done_encoding();
- BitOut::close();
- }
-
- void ArithmCodingIn::close(void)
- {
- Coding_status = EoF;
- BitIn::close();
- }
-
- // Put a symbol into the coded stream
- void ArithmCodingOut::put(const Symbol symbol)
- {
- Index index = Source_model.get_index(symbol);
- encode_index(index);
- Source_model.update_model(index);
- }
-
- Symbol ArithmCodingIn::get(void)
- {
- assure( Coding_status != EoF, "Cannot read past End-of-File" );
- Index index = decode_index();
- if( index == Source_model.eof_index() )
- {
- Coding_status = EoF;
- return 0;
- }
- // Note, update_model may change
- Symbol symbol = Source_model.get_symbol(index);
- Source_model.update_model(index); // symbol vs. index mapping, that's why
- // get_symbol should precede update_mod
- return symbol;
- }
-
- /*
- *------------------------------------------------------------------------
- * Encoding an item
- */
-
- // Output a bit following by
- // bits_to_follow opposite bits
- inline void ArithmCodingOut::put_bit_plus_follow(const char bit)
- {
- // message("\nWrite 1 + %d bits",bits_to_follow);
- put_bit(bit);
- for(; bits_to_follow > 0; bits_to_follow--)
- put_bit(!bit); // The loop sets bits_to_follow to 0
- }
-
- void ArithmCodingOut::encode_index(const Index index)
- {
- Code range = high - low + 1; // Current range
- // Narrow the code region to that
- // allotted to this symbol
- // First compute a new low end
- low = low + (range * Source_model.cum_freq(index)) /
- Source_model.total_cum_freq();
- // and add the new range to it
- high = low + (range * Source_model.freq(index)) /
- Source_model.total_cum_freq() - 1;
- if( high <= low )
- _error("ArithmCoder went crazy: high <= low!\n"
- "index = %d, symbol = %d, freq = %d, cum_freq = %d total = %d\n",
- "range = %d (0x%6x), high = 0x%6x, low = 0x%6x",
- index, index == Source_model.eof_index() ? 0 :
- Source_model.get_symbol(index),
- Source_model.freq(index), Source_model.cum_freq(index),
- Source_model.total_cum_freq(), range, range, high, low);
-
- #if 0
- message("\n\n-----\nindex %d, symbol %d, freq %d, cum_freq %d",
- index, index == Source_model.eof_index() ? 0 :
- Source_model.get_symbol(index),
- Source_model.freq(index), Source_model.cum_freq(index));
- message("\nhigh %6x, low %6x", high, low);
- #endif
-
- for(;;) // Loop to output bits we're positive about
- {
- if( !(high & Half) )/*high < Half*/ // If the region is entirely in low
- put_bit_plus_follow(0); // half, binary expansion starts w/ 0
- else if( low & Half ) // low >= Half, since Top_value = 1<<Codesize-1
- { // Upper half binary numbers begin
- put_bit_plus_follow(1); // positively with 1, so output it
- low &= ~Half, high &= ~Half; // Subtract the offset to top
- }
- // if( low <= First_qtr && high < Third_qtr )
- else if( low & First_qtr & (high ^ Third_qtr) )
- { // Cannot tell the bit now, but
- bits_to_follow++; // the next bit would be opposite
- low -= First_qtr, high -= First_qtr; // Subtract offset to middle
- }
- else
- break;
- low = low << 1; // Scale up the range, shifting in
- high = (high << 1) | 1; // 1 as the LSB for high
- }
- }
-
-
- /*
- *------------------------------------------------------------------------
- * Finish encoding the stream
- * Output two bits that select the quarter that contains the current
- * code range. Note that the decoder part of the codec analyzes bits when
- * they are loaded into the 'value' variable, which acts like
- * sort of the buffer. We're analyzing a couple of bits off the top of
- * the buffer, shift them out and shift in some bits from the input stream.
- * But if the input stream contains only, say, 2 bits that specify the
- * termination, we still need to shift them on the top of the buffer.
- * I.e. we need to read up to Code_size-2 phony bits after the EOF (that's
- * what has been suggested in the 'Text Compression' book).
- * However, reading "past" actual EOF makes it impossible to concatenate
- * two ArithmCoding streams (or write anything after the encoded stream
- * for that matter). Instead of 'reading' phony bits after the EOF,
- * we'd rather write some extra bits.
- * So, after the encoding is finished, we still need to output enough
- * 'phony' bits to prevent EOF on reading. On the other hand, we need
- * to make sure that *all* these bits are going be read back on decoding.
- */
-
- void ArithmCodingOut::done_encoding(void)
- {
- bits_to_follow += 1;
- if( low < First_qtr )
- put_bit_plus_follow(0);
- else
- put_bit_plus_follow(1);
-
- register int i; // Output phony bits
- for(i=0; i<Code_size-2; i++)
- put_bit_plus_follow(0);
- }
-
- /*
- *------------------------------------------------------------------------
- * Decoding an item from the input stream
- */
-
- Index ArithmCodingIn::decode_index()
- {
- Code range = high - low + 1; // Current code region
- // Find the cumulative freq for the
- // symbol
- Code cum = ((value-low+1)*Source_model.total_cum_freq() - 1) /
- range;
- Index index = Source_model.find_index(cum);
-
- // Narrow the code region to that
- // allotted to this symbol
- // First compute a new low end
- low = low + (range * Source_model.cum_freq(index)) /
- Source_model.total_cum_freq();
- // and add the new range to it
- high = low + (range * Source_model.freq(index)) /
- Source_model.total_cum_freq() - 1;
- assert( high > low );
-
- for(;;) // Simulate the encoder and get rid of bits
- { // encoder has put for 'index'
- if( !(high & Half) ) // if( high < Half )
- ; // Expand the low half of code region
- else if( low & Half ) // if( low >= Half )
- { // Expand the upper half
- value &= ~Half;
- low &= ~Half, high &= ~Half;
- }
- else if( low & First_qtr & (high ^ Third_qtr) )
- { // if( low >= First_qtr && high < Third_qtr )
- value -= First_qtr; // Expand middle half
- low -= First_qtr, high -= First_qtr;
- }
- else
- break;
- low = low << 1; // Scale up the range, shifting in
- high = (high << 1) | 1; // 1 as the LSB for high
- value = (value << 1) | get_bit();; // Move in the next input bit
- }
-
- return index;
- }
-
-